package com.zhangyj.applets.swing;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.text.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
import javax.swing.border.*;
import com.sun.java.swing.plaf.motif.*;
import com.sun.java.swing.plaf.windows.*;

/**
 * <p>
 * Title:OpenSwing
 * </p>
 * <p>
 * Description: JDatePicker 日期选择框<BR>
 */
public final class JDatePicker extends JComboBox implements Serializable {

    private static final long serialVersionUID = 7223087251582047449L;
    public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    public static final SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM");
    public static final SimpleDateFormat dayFormat = new SimpleDateFormat("d");

    /**
     * 构造式
     */
    public JDatePicker() {
        setEditable(true);
        JTextField textField = ((JTextField) this.getEditor().getEditorComponent());
        textField.setDocument(new JDateDocument(textField));
        setSelectedItem(dateFormat.format(new Date()));
        
    }

    /**
     * 取得当前选择的日期
     * 
     * @return Date
     */
    public Date getSelectedDate() {
        try {
            return dateFormat.parse(this.getSelectedItem().toString());
        } catch (Exception ex) {
            return new Date();
        }
    }

    /**
     * 设置当前选择的日期
     * 
     * @return Date
     */
    public void setSelectedDate(Date date) {
        setSelectedItem(date);
    }

    /**
     * 设置当前选取的日期
     * 
     * @param item
     *            Object
     */
    @Override
    public void setSelectedItem(Object item) {
        removeAllItems();
        addItem(item);
        super.setSelectedItem(item);
    }

    /**
     * 更新UI
     */
    @Override
    public void updateUI() {
        ComboBoxUI cui = (ComboBoxUI) UIManager.getUI(this);
        if (cui instanceof MetalComboBoxUI) {
            cui = new MetalDateComboBoxUI();
        } else if (cui instanceof MotifComboBoxUI) {
            cui = new MotifDateComboBoxUI();
        } else if (cui instanceof WindowsComboBoxUI) {
            cui = new WindowsDateComboBoxUI();
        }
        setUI(cui);
    }

    class MetalDateComboBoxUI extends MetalComboBoxUI {

        @Override
        protected ComboPopup createPopup() {
            return new DatePopup(comboBox);
        }

        @Override
        protected JButton createArrowButton() {
            return super.createArrowButton();
        }
    }

    class WindowsDateComboBoxUI extends WindowsComboBoxUI {

        @Override
        protected ComboPopup createPopup() {
            return new DatePopup(comboBox);
        }

        @Override
        protected JButton createArrowButton() {
            return super.createArrowButton();
        }
    }

    class MotifDateComboBoxUI extends MotifComboBoxUI {

        private static final long serialVersionUID = -5834061519799365877L;

        @Override
        protected ComboPopup createPopup() {
            return new DatePopup(comboBox);
        }

        @Override
        protected JButton createArrowButton() {
            return super.createArrowButton();
        }
    }

    public static final class JDateDocument extends PlainDocument {

        /**
         * 
         */
        private static final long serialVersionUID = -5201049047999925742L;
        private JTextComponent textComponent; // 日期输入文本框
        private int newOffset; // 新的插入位置

        /******************************************************************
         ** 函数名称：public JDateDocument(JTextComponent tc) 功能描述：设置此文本框显示的默认值和格式限制
         * 入口参数：tc : JTextComponent类型,当前操作的文本框 返回值：无 调用者：类JDateDocument
         *******************************************************************/
        public JDateDocument(JTextComponent tc) {
            // 保存操作的文本框
            textComponent = tc;
            // 获取当前日期
            String strCurrentDate = getCurrentDate();
            // 设置显示为当前日期,同时完成显示的格式化
            try {
                insertString(0, strCurrentDate, null);
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }

        /******************************************************************
         ** 函数名称：public void insertString(int offset, String s, AttributeSet
         * attributeSet) throws BadLocationException 功能描述：重载原方法,限制用户插入格式为日期格式
         * 入口参数：offset: int型,插入位置 s: String型,插入字符串 attributeSet:
         * AttributeSet型,属性集 返回值：无 调用者：类JDateDocument
         *******************************************************************/
        @Override
        public void insertString(int offset, String s, AttributeSet attributeSet) throws BadLocationException {
            String toTest; // 用于测试输入合法性的字符串
            // 判断插入字符串长度
            if (s.length() == 1) {
                // 长度为1
                try {
                    // 限制输入为整数
                    Integer.parseInt(s);
                } catch (Exception ex) {
                    // 错误则提示并返回
                    Toolkit.getDefaultToolkit().beep();
                    return;
                }
                // 取得原始插入位置
                newOffset = offset;
                // 如果插入位置为"-"符号的前面,则移动到其后面插入(改变newOffset的值)
                if (offset == 4 || offset == 7) {
                    newOffset++;
                    textComponent.setCaretPosition(newOffset);
                }
                // 如果插入位置为最后,则不插入
                if (offset == 10) {
                    return;
                }
                // 取得显示的时间,处理后得到要显示的字符串
                toTest = textComponent.getText();
                toTest = toTest.substring(0, newOffset) + s + toTest.substring(newOffset + 1, 10);
                // 如果要显示的字符串合法,则显示,否则给出提示并退出
                if (!isLegalDate(toTest)) {
                    Toolkit.getDefaultToolkit().beep();
                    return;
                }
                // 插入字符串
                super.remove(newOffset, 1);
                super.insertString(newOffset, s, attributeSet);
            } // 如果插入长度10
            else if (s.length() == 10) {
                // 合法则显示,否则给出提示退出
                if (!isLegalDate(s)) {
                    Toolkit.getDefaultToolkit().beep();
                    return;
                }
                // 插入字符串
                super.remove(0, getLength());
                super.insertString(0, s, attributeSet);
            }
        }

        /**********************************************************************************
         ** 函数名称：public void remove(int offset, int length) throws
         * BadLocationException 功能描述：重载原方法,删除合适位置的字符串 入口参数：offset: int型,插入位置
         * length: int型,删除长度 返回值：无 调用者：insertString(int, String,AttributeSet)
         ***********************************************************************************/
        @Override
        public void remove(int offset, int length) throws BadLocationException {
            // 如果插入位置在"-"前,则回退一个光标位置
            if (offset == 4 || offset == 7) {
                textComponent.setCaretPosition(offset - 1);
            } else {
                textComponent.setCaretPosition(offset);
            }
        }

        /**********************************************************************************
         ** 函数名称：public boolean isLegalDate(String strDate)
         * 功能描述：判断插入的长度为10的字符串是否合法 入口参数：intY: int型,年的值 intM: int型,月的值 intD:
         * int型,日的值 返回值：boolean型,真,表示是合法的,假,表示不合法 调用者：insertString(int,
         * String,AttributeSet)
         ***********************************************************************************/
        public boolean isLegalDate(String strDate) {
            int intY, intM, intD; // 年,月,日的值
            int iCaretPosition; // 光标位置
            // 获取字符串
            strDate = strDate.trim();
            // 如果为空,长度不对,则为非法,返回false
            if (strDate == null || strDate.trim().equals("") || strDate.trim().length() != 10) {
                return false;
            }
            // 如果是全角字符,则返回false
            for (int i = 0; i < 10; i++) {
                if (((int) strDate.charAt(i)) > 255) {
                    return false;
                }
            }
            // 取年,月,日的值
            try {
                intY = Integer.parseInt(strDate.substring(0, 4));
                intM = Integer.parseInt(strDate.substring(5, 7));
                intD = Integer.parseInt(strDate.substring(8));
            } catch (Exception e) {
                // 失败则返回false
                return false;
            }
            // 数值越界,则修改至正确值
            // 保存光标位置
            iCaretPosition = textComponent.getCaretPosition();
            // 年越界
            // if(intY > 2999){
            // //修改为合法值
            // textComponent.setText("2999" + strDate.substring(5));
            // //手动设置光标位置
            // textComponent.setCaretPosition(iCaretPosition + 1);
            // return false;
            // }
            // if(intY 1000){
            // textComponent.setText("1000" + strDate.substring(5));
            // textComponent.setCaretPosition(iCaretPosition + 1);
            // return false;
            // }
            // 月越界
            if (intM > 12) {
                textComponent.setText(strDate.substring(0, 5) + "12" + strDate.substring(7));
                textComponent.setCaretPosition(iCaretPosition + 1);
                return false;
            }
            if (intM < 1) {
                textComponent.setText(strDate.substring(0, 5) + "01" + strDate.substring(7));
                textComponent.setCaretPosition(iCaretPosition + 1);
                return false;
            }
            // 根据月份,判断日期输入,如越界,则修改
            switch (intM) {
                // 最大天数为31天
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                case 10:
                case 12:

                    // 如果输入大于31,则修改为31
                    if (intD > 31) {
                        textComponent.setText(strDate.substring(0, 8) + "31");
                        textComponent.setCaretPosition(iCaretPosition + 1);
                    }
                    if (intD < 1) {
                        textComponent.setText(strDate.substring(0, 8) + "01");
                        textComponent.setCaretPosition(iCaretPosition + 1);
                    }
                    return (intD <= 31 && intD > 0);
                // 最大天数为30天
                case 4:
                case 6:
                case 9:
                case 11:

                    // 如果输入大于30,则修改为30
                    if (intD > 30) {
                        textComponent.setText(strDate.substring(0, 8) + "30");
                        textComponent.setCaretPosition(iCaretPosition + 1);
                    }
                    if (intD < 1) {
                        textComponent.setText(strDate.substring(0, 8) + "01");
                        textComponent.setCaretPosition(iCaretPosition + 1);
                    }
                    return (intD <= 30 && intD > 0);
                // 2月份
                case 2:

                    // 区别闰年
                    if ((intY % 4 == 0 && intY % 100 != 0) || intY % 400 == 0) {
                        // 如果输入大于29,则修改为29
                        if (intD > 29) {
                            textComponent.setText(strDate.substring(0, 8) + "29");
                            textComponent.setCaretPosition(iCaretPosition + 1);
                        }
                        if (intD < 1) {
                            textComponent.setText(strDate.substring(0, 8) + "01");
                            textComponent.setCaretPosition(iCaretPosition + 1);
                        }
                        return (intD <= 29 && intD > 0);
                    } else {
                        // 如果输入大于28,则修改为28
                        if (intD > 28) {
                            textComponent.setText(strDate.substring(0, 8) + "28");
                            textComponent.setCaretPosition(iCaretPosition + 1);
                        }
                        if (intD < 1) {
                            textComponent.setText(strDate.substring(0, 8) + "01");
                            textComponent.setCaretPosition(iCaretPosition + 1);
                        }
                        return (intD <= 28 && intD > 0);
                    }
                // 默认返回假
                default:
                    return false;
            }
        }

        public static String getCurrentDate() {
            return (new java.text.SimpleDateFormat("yyyy-MM-dd")).format(new java.util.Date());
        }
    }

    /**
     * <p>
     * Title: JDatePicker
     * </p>
     * <p>
     * Description: DatePopup 选择框弹出的日期选择面板
     * </p>
     * <p>
     * Copyright: Copyright (c) 2004
     * </p>
     * <p>
     * Company:
     * </p>
     * 
     * @author <a href="mailto:sunkingxie@hotmail.com"'>Sunking</a>
     * @version 1.0
     */
    final class DatePopup extends BasicComboPopup {

        /**
         * 
         */
        private static final long serialVersionUID = 3798936390586924472L;
        protected Calendar calendar;
        protected JLabel monthLabel;
        protected JPanel days = null;
        protected Color selectedBackground;
        protected Color selectedForeground;
        protected Color background;
        protected Color foreground;
        ImageIcon todayIcon;

        public DatePopup(JComboBox box) {
            super(box);
            todayIcon = null;// OpenSwingUtil.getOpenSwingImage("today.gif", new
            // ImageIcon());
            calendar = Calendar.getInstance();
            background = UIManager.getColor("ComboBox.background");
            foreground = UIManager.getColor("ComboBox.foreground");
            selectedBackground = UIManager.getColor("ComboBox.selectionBackground");
            selectedForeground = UIManager.getColor("ComboBox.selectionForeground");
            initializePopup();
        }

        /**
         * 初始化弹出面板
         */
        protected void initializePopup() {
            // < 9999/99/99 > >>
            JPanel header = new JPanel();
            header.setLayout(new BoxLayout(header, BoxLayout.X_AXIS));
            header.setBackground(new Color(0, 0, 128));
            header.setForeground(Color.white);
            header.setPreferredSize(new Dimension(1, 25));

            JLabel label;
            label = createUpdateButton(Calendar.YEAR, -1);
            label.setText(" << ");
            header.add(Box.createHorizontalStrut(12));
            header.add(label);
            header.add(Box.createHorizontalStrut(12));

            label = createUpdateButton(Calendar.MONTH, -1);
            label.setText(" < ");
            header.add(label);

            monthLabel = new JLabel("", JLabel.CENTER);
            monthLabel.setBackground(new Color(0, 0, 128));
            monthLabel.setForeground(Color.white);
            header.add(Box.createHorizontalGlue());
            header.add(monthLabel);
            header.add(Box.createHorizontalGlue());

            label = createUpdateButton(Calendar.MONTH, 1);
            label.setText(" > ");
            header.add(label);

            label = createUpdateButton(Calendar.YEAR, 1);
            label.setText(" >> ");

            header.add(Box.createHorizontalStrut(12));
            header.add(label);
            header.add(Box.createHorizontalStrut(12));

            // 星期日 星期一 星期二 星期三 星期四 星期五 星期六
            JPanel pWeeks = new JPanel(new GridLayout(0, 7));
            pWeeks.setBackground(background);
            pWeeks.setOpaque(true);
            DateFormatSymbols sy = new DateFormatSymbols(Locale.getDefault());
            String strWeeks[] = sy.getShortWeekdays();
            for (int i = 1; i <= 7; i++) {
                label = new JLabel();
                label.setHorizontalAlignment(JLabel.CENTER);
                label.setForeground(foreground);
                if (Locale.getDefault().equals(Locale.SIMPLIFIED_CHINESE) || Locale.getDefault().equals(Locale.TRADITIONAL_CHINESE)) {
                    if (strWeeks[i].length() == 3) {
                        strWeeks[i] = strWeeks[i].substring(2);
                    }
                }
                label.setText(strWeeks[i]);
                pWeeks.add(label);
            }
            // 中间放日期的面板
            days = new JPanel(new GridLayout(0, 7));
            days.setBorder(new TopBottomLineBorder(Color.black));
            days.setBackground(background);
            days.setOpaque(true);
            JPanel pCenter = new JPanel(new BorderLayout());
            pCenter.setBackground(background);
            pCenter.setOpaque(true);
            pCenter.add(pWeeks, BorderLayout.NORTH);
            pCenter.add(days, BorderLayout.CENTER);
            // 显示今天的日期,直接单击图标跳到今天
            JLabel lbToday = new JDayLable(new Date(), "Today:" + dateFormat.format(new Date()));
            lbToday.setIcon(todayIcon);
            lbToday.setHorizontalAlignment(JLabel.LEFT);
            JPanel pSouth = new JPanel(new BorderLayout());
            pSouth.setBackground(background);
            pSouth.setForeground(foreground);
            pSouth.add(lbToday, BorderLayout.CENTER);

            setForeground(foreground);
            setBackground(background);
            setBorder(BorderFactory.createLineBorder(Color.black));
            setLayout(new BorderLayout());
            removeAll();
            add(BorderLayout.NORTH, header);
            add(BorderLayout.CENTER, pCenter);
            add(BorderLayout.SOUTH, pSouth);
        }

        /**
         * 显示弹出面板
         */
        @Override
        public void show() {
            updatePopup();
            super.show();
        }

        /**
         * 创建上一月,下一月,上一年,下一年"按钮"
         * 
         * @param field
         *            int
         * @param amount
         *            int
         * @return JLabel
         */
        protected JLabel createUpdateButton(final int field, final int amount) {
            final JLabel label = new JLabel();
            final Border selectedBorder = new LineBorder(Color.black);
            final Border unselectedBorder = new EmptyBorder(selectedBorder.getBorderInsets(new JLabel()));
            label.setBorder(unselectedBorder);
            label.setBackground(new Color(0, 0, 128));
            label.setForeground(Color.white);
            label.setRequestFocusEnabled(false);
            label.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseReleased(MouseEvent e) {
                    calendar.add(field, amount);
                    updatePopup();
                }

                @Override
                public void mouseEntered(MouseEvent e) {
                    label.setBorder(selectedBorder);
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    label.setBorder(unselectedBorder);
                }
            });
            return label;
        }

        /**
         * 更新日期
         */
        protected void updatePopup() {
            monthLabel.setText(monthFormat.format(calendar.getTime()));
            days.removeAll();

            Calendar setupCalendar = (Calendar) calendar.clone();
            setupCalendar.set(Calendar.DAY_OF_MONTH, 1);
            int first = setupCalendar.get(Calendar.DAY_OF_WEEK);
            setupCalendar.add(Calendar.DATE, -first);
            int flag = 0;
            for (int i = 0; i < 42; i++) {
                setupCalendar.add(Calendar.DATE, 1);
                String txt = dayFormat.format(setupCalendar.getTime());
                JLabel label = new JDayLable(setupCalendar.getTime(), txt);
                days.add(label);
                if (txt.equals("1")) {
                    flag++;
                }
                if (flag != 1) {
                    label.setEnabled(false);
                }
            }
            days.repaint();
            pack();
        }

        /**
         * <p>
         * Title: OpenSwing
         * </p>
         * <p>
         * Description: TopBottomLineBorder只带上下两条线的边界Border
         * </p>
         * <p>
         * Copyright: Copyright (c) 2004
         * </p>
         * <p>
         * Company:
         * </p>
         * 
         * @author <a href="mailto:sunkingxie@hotmail.com">SunKing</a>
         * @version 1.0
         */
        class TopBottomLineBorder extends AbstractBorder {

            /**
             * 
             */
            private static final long serialVersionUID = -4744625790306223474L;
            private Color lineColor;

            public TopBottomLineBorder(Color color) {
                lineColor = color;
            }

            @Override
            public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
                g.setColor(lineColor);
                g.drawLine(0, 0, c.getWidth(), 0);
                g.drawLine(0, c.getHeight() - 1, c.getWidth(), c.getHeight() - 1);
            }
        }

        /**
         * <p>
         * Title: JDatePicker
         * </p>
         * <p>
         * Description:JDayLable 带选取日期功能的JLabel
         * </p>
         * <p>
         * Copyright: Copyright (c) 2004
         * </p>
         * <p>
         * Company:
         * </p>
         * 
         * @author <a href="mailto:sunkingxie@hotmail.com"'>Sunking</a>
         * @version 1.0
         */
        class JDayLable extends JLabel implements MouseListener {

            /**
             * 
             */
            private static final long serialVersionUID = 4016903738245179981L;
            Date date;
            Image img;
            boolean isToday = false;

            public JDayLable(Date date, String text) {
                setHorizontalAlignment(JLabel.CENTER);
                setForeground(foreground);
                setPreferredSize(new Dimension(30, 20));
                setToolTipText(dateFormat.format(date));
                this.date = date;
                setText(text);
                Date d = new Date();
                isToday = (text.length() > 3) && dateFormat.format(date).equals(dateFormat.format(d));
                if (calendar.getTime().equals(date)) {
                    setBorder(new LineBorder(selectedBackground, 1));
                }
                addMouseListener(this);
            }

            @Override
            public void mousePressed(MouseEvent e) {
            }

            @Override
            public void mouseClicked(MouseEvent e) {
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (isEnabled()) {
                    setOpaque(false);
                    setBackground(background);
                    setForeground(foreground);
                }
                calendar.setTime(date);

                if (comboBox.isEditable() && comboBox.getEditor() != null) {
                    comboBox.configureEditor(comboBox.getEditor(), dateFormat.format(calendar.getTime()));
                }
                comboBox.setSelectedItem(dateFormat.format(calendar.getTime()));
                comboBox.setPopupVisible(false);
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                if (isEnabled()) {
                    setOpaque(true);
                    setBackground(selectedBackground);
                    setForeground(selectedForeground);
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {
                if (isEnabled()) {
                    setOpaque(false);
                    setBackground(background);
                    setForeground(foreground);
                }
            }

            @Override
            public void paint(Graphics g) {
                super.paint(g);
                if (isToday && todayIcon != null && isEnabled()) {
                    int x = (this.getWidth() - todayIcon.getIconWidth()) / 2;
                    int y = (this.getHeight() - todayIcon.getIconHeight()) / 2;
                    todayIcon.paintIcon(this, g, x, y);
                }
            }
        }
    }

    /**
     * 测试JDatePicker
     */
    public static void main(String args[]) {
        String windows = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
        // String metal="javax.swing.plaf.metal.MetalLookAndFeel";
        // String motif="com.sun.java.swing.plaf.motif.MotifLookAndFeel";
        try {
            UIManager.setLookAndFeel(windows);
            // SwingUtilities.updateComponentTreeUI(this);
        } catch (Exception e) {
            System.out.println("不能设置这个界面风格:" + e);
        }

        JFrame f = new JFrame("JDatePicker Demo");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel c = new JPanel();
        c.add(new JLabel("From:"));
        JDatePicker datePickerFrom = new JDatePicker();
        c.add(datePickerFrom);
        c.add(new JLabel("To:"));
        JDatePicker datePickerTo = new JDatePicker();
        datePickerTo.getSelectedItem();
        datePickerTo.setEditable(false);
        c.add(datePickerTo);
        f.getContentPane().add(c, BorderLayout.NORTH);

        f.getContentPane().add(new JDatePicker(), BorderLayout.SOUTH);

        final JTable table = new JTable(20, 10);
        JComboBox editor = new JDatePicker();
        editor.setBorder(null);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        table.setDefaultEditor(Object.class, new DefaultCellEditor(editor));
        JScrollPane sp = new JScrollPane(table);
        f.getContentPane().add(sp, BorderLayout.CENTER);

        f.setSize(600, 400);
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        f.setLocation((screenSize.width - f.getWidth()) / 2, (screenSize.height - f.getHeight()) / 2);
        f.setVisible(true);
    }
}
